/*
 * Copyright (C) 2008 Peter Kling
 *
 * This file is part of 'A Kind of Billiard', a graphical billiard simulation.
 *
 * 'A Kind of Billiard' is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 *
 * 'A Kind of Billiard' is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * 'A Kind of Billiard'. If not, see <http://www.gnu.org/licenses/>.
 */

package cg2.particles;
/*
 * Created on Mar 3, 2004
 *
 * @author Irene Kam  (kaminc@cox.net)
 */

import cg2.mathematics.MathVector;

/**
 * This is a particle Object, which holds all the property of one particle. 
 */
public class Particle {
	
	//MagicNumber Definition: Influences the RandomValue returned by the getRandomValue() Method.
	public static final int FIX_GRAVITY = 0;
	public static final int ONE_SIDE_GRAVITY = 1;
	public static final int TWO_SIDE_GRAVITY = 2;
	public static final int SINUS_CURVE_WITH_EXP = 3;
	public static final int SINUS_CURVE = 4;
	
   // This Particle's life. Full life has the value 1.0f.
   protected double life;
   // Fade speed (i.e. how fast this particle's life decreases).
   protected double fade;
   // Red, green, and blue colors for this particle.
   protected double red;
   protected double green;
   protected double blue;
   protected double grey = 0.5f;
   // X, Y, Z locations for this particle.
   protected MathVector position = new MathVector(3);
   
   //TODO: Do I need speed? I think in the moment we only use gravity.. X, Y, Z directions and speeds for this particle.
   protected MathVector speed= new MathVector(3);

   // X, Y, Z gravity on this particle(direction and pull force).
   protected MathVector gravity= new MathVector(3);
   
   protected double xGravity,yGravity,zGravity;
   
   // 0 : Assign Fix Value
   // 1: Assign between 0 and Value
   // 2: Assign between -Value and Value
   protected int[] randomGravity = { 0, 0, 0 };

   //Slowdown factor: Is given by Particle Engine)
   protected double slowdown;
   //Size of the particle
   protected double size;
   // The parent object.
   ParticleEngine m_oParent;
   
   //WarmUp-Phase
   protected boolean visible = false;

   /**
    * Creates a Particle instance with it's properties initialized.
    */
   public Particle(ParticleEngine parent) {
      m_oParent = parent;
      // Initialize the property values.
      speed = new MathVector(3);
      speed.set(0, 0);
      speed.set(1, 0);
      speed.set(2, 0);
      gravity = new MathVector(3);
      gravity.set(0,0);
      gravity.set(1,0);
      gravity.set(2,0);
      slowdown = m_oParent.getSlowDown();
      size = m_oParent.getParticleSize();
      resetCommonProperties();
      
   }

   /**
    * Resets the life, fade, color, and location of this Particle object.    *
    */
   protected void resetCommonProperties() {
      // Give the particle full life when created.
      life = 1.0f;
      // Give the particle random fade value.
      fade = getRandomFadeValue();
      // Set color values
      red = getGreyValue();
      green = getGreyValue();
      blue = getGreyValue();
      // Initial location for the particle is at 0,0,0.
      position.set(0,0);
      position.set(1,0);
      position.set(2,0);
   }

   /**
    * Restarts this particle. 
    * .
    */
   protected void restart() {
	  visible = true;
      resetCommonProperties();
      // Assign direction and speed for this particle.
      speed.set(0,0);
      speed.set(1,0);
      speed.set(2,0);
      // Assign gravity. If Random, then a random value between 0 and Gravity is chosen.
      // Otherwise fixed gravity is assigned
      if (randomGravity[0]>0)
    	  gravity.set(0,getRandomValue(randomGravity[0],xGravity,0));
      else
    	  gravity.set(0,xGravity);
      if (randomGravity[1]>0)
    	  gravity.set(1,getRandomValue(randomGravity[1],yGravity,0));
      else
    	  gravity.set(1,yGravity);
      if (randomGravity[2]>0)
    	  gravity.set(2,getRandomValue(randomGravity[2],zGravity,0));
      else
    	  gravity.set(2,zGravity);
      
      slowdown = m_oParent.getSlowDown();
      size = m_oParent.getParticleSize();
   }
   
   /**
    * Moves the particle, updates Speed and Life
    */
   public void moveParticle(){
	   MathVector slowDown = speed.clone();
	   
	   //Implement Sinus-Curve for Smoke-Like Movement.
	   
	   slowDown.add(gravity);
	   slowDown.mul(1/(slowdown*1000));
	   position.add(slowDown);
	   
	   life -= fade;
	   if (life < 0)
		   restart();
   }

   /**
    * Generates a random value for the particle's fade speed.
    * 
    * @return A random double value.
    */
   protected double getRandomFadeValue() {
      return (double) (Math.random() * 100) / 10000f + .0005f;
   }

   
   protected double getGreyValue(){
	   return grey;
   }


   /**
    * Returns a random double value between 0 inclusive and maxSpeed exclusive.
    * @param magicNumber 1 for random Value between 0 and maxSpeed, 2 for -maxSpeed to maxSpeed
    * @param maxSpeed
    * @param time
    * @return
    */
   protected double getRandomValue(int magicNumber, double maxSpeed, double time) {
      if (magicNumber == 1) {
         return (double) Math.random()*maxSpeed;
      } else if (magicNumber == 2){
    	 return (double) Math.random()*maxSpeed* ((Math.random() >= 0.5) ? 1 : -1);
      } else if (magicNumber == 0){
    	  return maxSpeed;
      } else if (magicNumber == 3){
    	  return Math.sin(time)*Math.exp(-time/10)*maxSpeed;
      }	else if (magicNumber == 4){
    	  return Math.sin(time)*maxSpeed;
      }
      return 1;
   }

   /**
    * Checks to see if this particle is alive. The particle is alive if
    * it's life is >= 0. In the first round, every particle is dead, because it is initially not visible to get a nicer start, instead of a big blub.
    * 
    * @return Returns true if particle is alive. Otherwise returns false.
    */
   public boolean isAlive() {
      if (life > 0.0f && visible) {
         return true;
      } else {
         return false;
      }
	   
   }

   /**
    * Retrieves it's blue intensity.
    * 
    * @return A double value for this particle's blue intensity.
    */
   public double getBlue() {
      return blue;
   }

   /**
    * Retrieves this particles' fade speed (how fast the particle dies).
    * 
    * @return A double value for this particle's fade speed.
    */
   public double getFade() {
      return fade;
   }

   /**
    * Retrieves this particle's green intensity.
    * 
    * @return A double value for this particle's green intensity.
    */
   public double getGreen() {
      return green;
   }

   /**
    * Retrieves this particle's life.
    * 
    * @return A double value for this particle's life.
    */
   public double getLife() {
      return life;
   }

   /**
    * Retrieves this particle's red intensity.
    * 
    * @return A double value for this particle's red intensity.
    */
   public double getRed() {
      return red;
   }

   /**
    * Sets this particle's blue intensity.
    * 
    * @parm A double value for this particle's blue intensity.
    */
   public void setBlue(double f) {
      blue = f;
   }

   /**
    * Sets this particle's fade speed.
    * 
    * @param A double value for the particle's fade speed.
    */
   public void setFade(double f) {
      fade = f;
   }

   /**
    * Sets this particle's green intensity.
    * 
    * @param A double value for this particle's green intensity.
    */
   public void setGreen(double f) {
      green = f;
   }

   /**
    * Sets this particle's life.
    * 
    * @param A double value for this particle's life.
    */
   public void setLife(double f) {
      life = f;
   }

   /**
    * Sets this particle's red intensity.
    * 
    * @param A double value for this particle's red intensity.
    */
   public void setRed(double f) {
      red = f;
   }

 
public double getSlowdown() {
	return slowdown;
}

public void setSlowdown(double slowdown) {
	this.slowdown = slowdown;
}

public double getSize() {
	return size;
}

public void setSize(double size) {
	this.size = size;
}

public MathVector getPosition() {
	return position;
}

public void setPosition(MathVector position) {
	this.position = position;
}

public MathVector getSpeed() {
	return speed;
}

public void setSpeed(MathVector speed) {
	this.speed = speed;
}

public MathVector getGravity() {
	return gravity;
}

public void setGravity(MathVector gravity) {
	this.gravity = gravity;
}

public double getXGravity() {
	return xGravity;
}

public void setXGravity(double gravity) {
	xGravity = gravity;
}

public double getYGravity() {
	return yGravity;
}

public void setYGravity(double gravity) {
	yGravity = gravity;
}

public double getZGravity() {
	return zGravity;
}

public void setZGravity(double gravity) {
	zGravity = gravity;
}

public int[] getRandomGravity() {
	return randomGravity;
}

public void setRandomGravity(int[] randomGravity) {
	this.randomGravity = randomGravity;
}

}
